-module({{classname}}).
-moduledoc """
Exposes the following operation IDs:
{{#operations}}{{#operation}}
- `{{httpMethod}}` to `{{{path}}}`, OperationId: `{{{operationIdOriginal}}}`:
{{summary}}.
{{notes}}
{{/operation}}{{/operations}}
""".

-behaviour(cowboy_rest).

-include_lib("kernel/include/logger.hrl").

%% Cowboy REST callbacks
-export([init/2]).
-export([allowed_methods/2]).
-export([content_types_accepted/2]).
-export([content_types_provided/2]).
-export([delete_resource/2]).
-export([is_authorized/2]).
-export([valid_content_headers/2]).
-export([handle_type_accepted/2, handle_type_provided/2]).

-ignore_xref([handle_type_accepted/2, handle_type_provided/2]).

-export_type([class/0, operation_id/0]).

-type class() :: '{{operations.pathPrefix}}'.

-type operation_id() ::
{{#operations}}{{#operation}}    {{^-first}}| {{/-first}}'{{operationIdOriginal}}'{{#-last}}.{{/-last}} %% {{summary}}
{{/operation}}{{/operations}}

-record(state,
        {operation_id :: operation_id(),
         accept_callback :: {{packageName}}_logic_handler:accept_callback(),
         provide_callback :: {{packageName}}_logic_handler:provide_callback(),
         api_key_callback :: {{packageName}}_logic_handler:api_key_callback(),
         context = #{} :: {{packageName}}_logic_handler:context()}).

-type state() :: #state{}.

-spec init(cowboy_req:req(), {{packageName}}_router:init_opts()) ->
    {cowboy_rest, cowboy_req:req(), state()}.
init(Req, {Operations, Module}) ->
    Method = cowboy_req:method(Req),
    OperationID = maps:get(Method, Operations, undefined),
    ?LOG_INFO(#{what => "Attempt to process operation",
                method => Method,
                operation_id => OperationID}),
    State = #state{operation_id = OperationID,
                   accept_callback = fun Module:accept_callback/4,
                   provide_callback = fun Module:provide_callback/4,
                   api_key_callback = fun Module:api_key_callback/2},
    {cowboy_rest, Req, State}.

-spec allowed_methods(cowboy_req:req(), state()) ->
    {[binary()], cowboy_req:req(), state()}.
{{#operations}}{{#operation}}allowed_methods(Req, #state{operation_id = '{{operationIdOriginal}}'} = State) ->
    {[<<"{{httpMethod}}">>], Req, State};
{{/operation}}{{/operations}}allowed_methods(Req, State) ->
    {[], Req, State}.

-spec is_authorized(cowboy_req:req(), state()) ->
    {true | {false, iodata()}, cowboy_req:req(), state()}.
{{#operations}}
{{#operation}}
{{#authMethods.size}}
is_authorized(Req0,
              #state{operation_id = '{{operationIdOriginal}}' = OperationID,
                     api_key_callback = Handler} = State) ->
    case {{packageName}}_auth:authorize_api_key(Handler, OperationID, {{#isApiKey.isKeyInQuery}}qs_val, {{/isApiKey.isKeyInQuery}}{{^isApiKey.isKeyInQuery}}header, {{/isApiKey.isKeyInQuery}}{{#isApiKey}}"{{keyParamName}}", {{/isApiKey}}{{^isApiKey}}<<"authorization">>, {{/isApiKey}}Req0) of
        {true, Context, Req} ->
            {true, Req, State#state{context = Context}};
        {false, AuthHeader, Req} ->
            {{false, AuthHeader}, Req, State}
    end;
{{/authMethods.size}}
{{/operation}}
{{/operations}}
is_authorized(Req, State) ->
    {true, Req, State}.

-spec content_types_accepted(cowboy_req:req(), state()) ->
    {[{binary(), atom()}], cowboy_req:req(), state()}.
{{#operations}}{{#operation}}content_types_accepted(Req, #state{operation_id = '{{operationIdOriginal}}'} = State) ->
    {{^consumes.size}}
    {[], Req, State};
    {{/consumes.size}}
    {{#consumes.size}}
    {[
    {{#consumes}}
      {<<"{{{mediaType}}}">>, handle_type_accepted}{{^-last}}{{#consumes.size}},{{/consumes.size}}{{/-last}}
    {{/consumes}}
     ], Req, State};
    {{/consumes.size}}
{{/operation}}{{/operations}}content_types_accepted(Req, State) ->
    {[], Req, State}.

-spec valid_content_headers(cowboy_req:req(), state()) ->
    {boolean(), cowboy_req:req(), state()}.
{{#operations}}{{#operation}}valid_content_headers(Req, #state{operation_id = '{{operationIdOriginal}}'} = State) ->
    {true, Req, State};
{{/operation}}{{/operations}}valid_content_headers(Req, State) ->
    {false, Req, State}.

-spec content_types_provided(cowboy_req:req(), state()) ->
    {[{binary(), atom()}], cowboy_req:req(), state()}.
{{#operations}}{{#operation}}content_types_provided(Req, #state{operation_id = '{{operationIdOriginal}}'} = State) ->
    {{^produces.size}}
    {[], Req, State};
    {{/produces.size}}
    {{#produces.size}}
    {[
    {{#produces}}
      {<<"{{{mediaType}}}">>, handle_type_provided}{{^-last}}{{#produces.size}},{{/produces.size}}{{/-last}}
    {{/produces}}
     ], Req, State};
    {{/produces.size}}
{{/operation}}{{/operations}}content_types_provided(Req, State) ->
    {[], Req, State}.

-spec delete_resource(cowboy_req:req(), state()) ->
    {boolean(), cowboy_req:req(), state()}.
delete_resource(Req, State) ->
    {Res, Req1, State1} = handle_type_accepted(Req, State),
    {true =:= Res, Req1, State1}.

-spec handle_type_accepted(cowboy_req:req(), state()) ->
    { {{packageName}}_logic_handler:accept_callback_return(), cowboy_req:req(), state()}.
handle_type_accepted(Req, #state{operation_id = OperationID,
                                 accept_callback = Handler,
                                 context = Context} = State) ->
    {Res, Req1, Context1} = Handler({{operations.pathPrefix}}, OperationID, Req, Context),
    {Res, Req1, State#state{context = Context1}}.

-spec handle_type_provided(cowboy_req:req(), state()) ->
    { {{packageName}}_logic_handler:provide_callback_return(), cowboy_req:req(), state()}.
handle_type_provided(Req, #state{operation_id = OperationID,
                                 provide_callback = Handler,
                                 context = Context} = State) ->
    {Res, Req1, Context1} = Handler({{operations.pathPrefix}}, OperationID, Req, Context),
    {Res, Req1, State#state{context = Context1}}.
